﻿using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using PaddleOCRSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;

namespace FeiYang
{
    static class 图像_
    {
        /// <summary>
        /// 查找图片，不能镂空
        /// </summary>
        /// <param name="图片路径"></param>
        /// <param name="搜索范围">如果为empty，则默认查找整个图像</param>
        /// <param name="匹配度">图片匹配度，默认90%</param>
        /// <returns>返回查找到的图片的中心点坐标</returns>
        /// 
        public static Point 查找图片(string 图片路径, Rectangle 搜索范围, double 匹配度 = 0.9)
        {
            var subBitmap = new Bitmap(图片路径);
            return 图像_.查找图片(subBitmap, 搜索范围, 匹配度);
        }

        public static void 截图(Rectangle 范围, string 名称)
        {
            using (Bitmap parBitmap = new Bitmap(范围.Width, 范围.Height))
            using (Graphics g = Graphics.FromImage(parBitmap))
            {
                g.CopyFromScreen(new Point(范围.Left, 范围.Top), new Point(0, 0), new Size(范围.Width, 范围.Height));
                parBitmap.Save(名称);
            }
        }

        public static Point 查找图片(Bitmap subBitmap, Rectangle 搜索范围, double 匹配度 = 0.9)
        {
            using (Bitmap parBitmap = new Bitmap(搜索范围.Width, 搜索范围.Height))
            using (Graphics g = Graphics.FromImage(parBitmap))
            {
                g.CopyFromScreen(new Point(搜索范围.Left, 搜索范围.Top), new Point(0, 0), new Size(搜索范围.Width, 搜索范围.Height));
                var p = 图像_.查找图片(subBitmap, parBitmap, 匹配度);
                if (p.X == -1 || p.Y == -1)
                {
                    return p;
                }
                else
                {
                    return new Point(搜索范围.X + p.X, 搜索范围.Y + p.Y);
                }
            }
        }

        public static Point 查找图片(string 图片路径, int 搜索x, int 搜索y, int 搜索x2, int 搜索y2, double 匹配度 = 0.9)
        {
            if (搜索x2 <= 搜索x || 搜索y2 <= 搜索y)
            {
                return new Point(-1, -1);
            }
            return 图像_.查找图片(图片路径, Rectangle.FromLTRB(搜索x, 搜索y, 搜索x2, 搜索y2), 匹配度);
        }

        public static Point 查找图片(Bitmap 图片, int 搜索x, int 搜索y, int 搜索x2, int 搜索y2, double 匹配度 = 0.9)
        {
            if (搜索x2 <= 搜索x || 搜索y2 <= 搜索y)
            {
                return new Point(-1, -1);
            }
            return 图像_.查找图片(图片, Rectangle.FromLTRB(搜索x, 搜索y, 搜索x2, 搜索y2), 匹配度);
        }

        public static Point 查找图片(string 图片路径, int 搜索x, int 搜索y, double 匹配度 = 0.9)
        {
            var subBitmap = new Bitmap(图片路径);
            return 图像_.查找图片(subBitmap, Rectangle.FromLTRB(搜索x, 搜索y, 搜索x + subBitmap.Width, 搜索y + subBitmap.Height), 匹配度);
        }

        public static Point 查找图片(Bitmap subBitmap, int 搜索x, int 搜索y, double 匹配度 = 0.9)
        {
            return 图像_.查找图片(subBitmap, Rectangle.FromLTRB(搜索x, 搜索y, 搜索x + subBitmap.Width, 搜索y + subBitmap.Height), 匹配度);
        }

        public static List<Point> 查找图像多个位置(Bitmap subBitmap, Bitmap parBitmap, double 匹配度 = 0.9)
        {
            Image<Bgr, byte> par = parBitmap.ToImage<Bgr, byte>();
            Image<Bgr, byte> sub = subBitmap.ToImage<Bgr, byte>();
            var r = par.MatchTemplate(sub, TemplateMatchingType.CcoeffNormed);
            r.MinMax(out _, out double[] maxValues, out _, out Point[] maxLocations);
            r.Dispose();
            List<Point> ret = new List<Point>();
            for (int i = 0; i < maxValues.Length; i++)
            {
                if (maxValues[i] >= 匹配度)
                {
                    ret.Add(maxLocations[i]);
                }
            }
            return ret;
        }

        public static Point 查找图片(Bitmap subBitmap, Bitmap parBitmap, double 匹配度 = 0.9)
        {
            Image<Bgr, byte> par = parBitmap.ToImage<Bgr, byte>();
            Image<Bgr, byte> sub = subBitmap.ToImage<Bgr, byte>();
            var r = par.MatchTemplate(sub, TemplateMatchingType.CcoeffNormed);
            r.MinMax(out _, out double[] maxValues, out _, out Point[] maxLocations);
            r.Dispose();
            for (int i = 0; i < maxValues.Length; i++)
            {
                if (maxValues[i] >= 匹配度)
                {
                    return maxLocations[i];
                }
            }
            return new Point(-1, -1);
        }

        public static Point 屏幕找色(string color, Rectangle 范围, double 匹配度 = 0.9)
        {
            return 图像_.屏幕找色(图像_.编译十六进制BGR颜色为ARGB颜色(color), 范围.Left, 范围.Top, 范围.Right, 范围.Bottom, 匹配度);
        }

        public static Point 屏幕找色(string color, int 搜索x, int 搜索y, int 搜索x2, int 搜索y2, double 匹配度 = 0.9)
        {
            return 图像_.屏幕找色(图像_.编译十六进制BGR颜色为ARGB颜色(color), 搜索x, 搜索y, 搜索x2, 搜索y2, 匹配度);
        }

        public  static Bgr 转换编译十六进制颜色字符串为BGR(string colorString)
        {
            return new Bgr(编译十六进制BGR颜色为ARGB颜色(colorString.Trim()));
        }

        public static Color 编译十六进制BGR颜色为ARGB颜色(string hex)
        {
            int argb = Convert.ToInt32("FF" + hex, 16);
            return Color.FromArgb((argb >> 24) & 0xff, argb & 0xff, (argb >> 8) & 0xff, (argb >> 16) & 0xff);
        }
        public static Point 屏幕找色(Color color, int 搜索x, int 搜索y, int 搜索x2, int 搜索y2, double 匹配度 = 0.9)
        {
            if (搜索x2 <= 搜索x || 搜索y2 <= 搜索y) return new Point(-1, -1);
            var 搜索范围 = Rectangle.FromLTRB(搜索x, 搜索y, 搜索x2, 搜索y2);
            using (Bitmap parBitmap = new Bitmap(搜索范围.Width, 搜索范围.Height))
            using (Graphics g = Graphics.FromImage(parBitmap))
            {
                g.CopyFromScreen(new Point(搜索范围.Left, 搜索范围.Top), new Point(0, 0), new Size(搜索范围.Width, 搜索范围.Height));
                var p = 图像_.屏幕找色(color, parBitmap, 匹配度);
                if (p.X == -1 || p.Y == -1)
                {
                    return p;
                }
                else
                {
                    return new Point(搜索x + p.X, 搜索y + p.Y);
                }
            }
        }

        public static List<Point> 屏幕找多点色(string color, int 搜索x, int 搜索y, int 搜索x2, int 搜索y2, double 匹配度 = 0.9)
        {
            return 屏幕找多点色(图像_.编译十六进制BGR颜色为ARGB颜色(color), 搜索x, 搜索y, 搜索x2, 搜索y2, 匹配度);
        }
        public static List<Point> 屏幕找多点色(Color color, int 搜索x, int 搜索y, int 搜索x2, int 搜索y2, double 匹配度 = 0.9)
        {
            if (搜索x2 <= 搜索x || 搜索y2 <= 搜索y) return new List<Point>();
            var 搜索范围 = Rectangle.FromLTRB(搜索x, 搜索y, 搜索x2, 搜索y2);
            using (Bitmap parBitmap = new Bitmap(搜索范围.Width, 搜索范围.Height))
            using (Graphics g = Graphics.FromImage(parBitmap))
            {
                g.CopyFromScreen(new Point(搜索范围.Left, 搜索范围.Top), new Point(0, 0), new Size(搜索范围.Width, 搜索范围.Height));
                return 图像_.屏幕找多点色(color, parBitmap, 匹配度);
            }
        }
        public static List<Point> 屏幕找多点色(Color color, Bitmap parBitmap, double 匹配度 = 0.9)
        {
            Bgr a = new Bgr(Math.Floor(color.B * 匹配度),  Math.Floor(color.G * 匹配度),  Math.Floor(color.R * 匹配度));
            Bgr b = new Bgr(color.B + Math.Floor(color.B * (1 - 匹配度)), color.G + Math.Floor(color.G * (1 - 匹配度)), color.R + Math.Floor(color.R * (1 - 匹配度)));
            var c = parBitmap.ToImage<Bgr, byte>().InRange(a, b);
            List<Point> r = new List<Point>();
            var 零值 = new Gray(0);
            for (int i = 0; i < c.Rows; i++)
            {
                for (int j = 0; j < c.Cols; j++)
                {
                    if (!c[i, j].Equals(零值))
                    {
                        r.Add(new Point(i, j));
                    }
                }
            }
            return r;
        }

        public static List<Point> 屏幕找多点多色(string colorStrs, int 搜索x, int 搜索y, int 搜索x2, int 搜索y2, double 匹配度 = 0.9)
        {
            if (搜索x2 <= 搜索x || 搜索y2 <= 搜索y) return new List<Point>();

            var 搜索范围 = Rectangle.FromLTRB(搜索x, 搜索y, 搜索x2, 搜索y2);
            using (Bitmap parBitmap = new Bitmap(搜索范围.Width, 搜索范围.Height))
            using (Graphics g = Graphics.FromImage(parBitmap))
            {
                g.CopyFromScreen(new Point(搜索范围.Left, 搜索范围.Top), new Point(0, 0), new Size(搜索范围.Width, 搜索范围.Height));
                return 图像_.屏幕找多点多色(colorStrs, parBitmap, 匹配度);
            }
        }

        public static List<Point> 屏幕找多点多色(string colorStrs, Bitmap parBitmap, double 匹配度 = 0.9)
        {
            Color[] colors = Array.ConvertAll(colorStrs.Split(','), (string a) =>
            {
                return 图像_.编译十六进制BGR颜色为ARGB颜色(a.Trim());
            });
            List<Point> ps = new List<Point>();
            foreach (var c in colors)
            {
                var p = 图像_.屏幕找多点色(c, parBitmap, 匹配度);
                ps.AddRange(p);
            }
            return ps;
        }

        public static bool 屏幕文字在一段时间内有变化(Rectangle 搜索范围, int 时长 = 1000)
        {
            var 开始 = DateTime.Now;
            int i = 0;
            int 结果 = 0;
            long 时差 = 0;
            while (时差 < 时长 && i < 4)
            {
                i++;
                var 内容 = 识别文字(搜索范围);
                Thread.Sleep(时长 / 4);
                var 内容2 = 识别文字(搜索范围);
                if (内容 != 内容2)
                {
                    结果++;
                }
                时差 = (long)(DateTime.Now - 开始).TotalMilliseconds;
            }
            return 结果 > 0;
        }
        public static Point 屏幕在一段时间内的变化点(Rectangle 搜索范围, int 时长 = 1000)
        {
            var a = new Bitmap(搜索范围.Width, 搜索范围.Height);
            using (Graphics g = Graphics.FromImage(a))
            {
                g.CopyFromScreen(new Point(搜索范围.Left, 搜索范围.Top), new Point(0, 0), new Size(搜索范围.Width, 搜索范围.Height));
                var 开始 = DateTime.Now;
                int i = 0;
                long 时差 = 0;
                while (时差 < 时长 && i < 4)
                {
                    i++;
                    Thread.Sleep(时长 / 4);
                    var b = new Bitmap(搜索范围.Width, 搜索范围.Height);
                    using (Graphics g2 = Graphics.FromImage(b))
                    {
                        g2.CopyFromScreen(new Point(搜索范围.Left, 搜索范围.Top), new Point(0, 0), new Size(搜索范围.Width, 搜索范围.Height));
                    }
                    var p = 内部函数(a.ToImage<Bgr, byte>(), b.ToImage<Bgr, byte>());
                    if (p.X != -1)
                    {
                        p = new Point(搜索范围.X + p.X, 搜索范围.Y + p.Y);
                        return p;
                    }
                    时差 = (long)(DateTime.Now - 开始).TotalMilliseconds;
                }
                return new Point(-1, -1);
            }

            Point 内部函数(Image<Bgr, byte> a1, Image<Bgr, byte> b1)
            {
                var c1 = a1.AbsDiff(b1);
                var d1 = c1.Convert<Gray, byte>().ThresholdBinary(new Gray(20), new Gray(255));
                var b = new Gray(0);
                for (int i = 0; i < d1.Rows; i++)
                {
                    for (int j = 0; j < d1.Cols; j++)
                    {
                        var bgr = d1[i, j];
                        if (!bgr.Equals(b))
                        {
                            return new Point(j, i);
                        }
                    }
                }
                return new Point(-1, -1);
            }
        }

        public static bool 比较一个位置执行操作后是否有变化(Rectangle rect, Func fn)
        {
            var a = new Bitmap(rect.Width, rect.Height);
            using (Graphics g = Graphics.FromImage(a))
            {
                g.CopyFromScreen(new Point(rect.Left, rect.Top), new Point(0, 0), new Size(rect.Width, rect.Height));
                fn();
                var b = new Bitmap(rect.Width, rect.Height);
                using (Graphics g2 = Graphics.FromImage(b))
                {
                    g2.CopyFromScreen(new Point(rect.Left, rect.Top), new Point(0, 0), new Size(rect.Width, rect.Height));
                    return 比较两张图像是否相同(a, b);
                }
            }
        }

        public static bool 比较两张图像是否相同(Bitmap a1, Bitmap b1)
        {
            return 比较两张图像是否相同(a1.ToImage<Bgr, byte>(), b1.ToImage<Bgr, byte>());
        }

        public static bool 比较两张图像是否相同(Image<Bgr, byte> a1, Image<Bgr, byte> b1)
        {
            var c1 = a1.AbsDiff(b1);
            var d1 = c1.Convert<Gray, byte>().ThresholdBinary(new Gray(20), new Gray(255));
            var b = new Gray(0);
            for (int i = 0; i < d1.Rows; i++)
            {
                for (int j = 0; j < d1.Cols; j++)
                {
                    var bgr = d1[i, j];
                    if (!bgr.Equals(b))
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        public static bool 屏幕在一段时间内有变化(Rectangle 搜索范围, int 时长 = 1000)
        {
            var p = 屏幕在一段时间内的变化点(搜索范围, 时长);
            return p.X != -1;
        }
        public static Hsv Color2Hsv(Color color)
        {
            var max = Math.Max(color.R, Math.Max(color.G, color.B));
            var min = Math.Min(color.R, Math.Max(color.G, color.B));
            var h = color.GetHue();
            var s = max == 0 ? 0 : 1f - (float)min / max;
            var v = (float)(max / 255f);
            Hsv lower = new Hsv(h, s, v);
            return lower;
        }

        public static Point 屏幕找色(Color color, Bitmap parBitmap, double 匹配度 = 0.9)
        {
            匹配度 = 1 - 匹配度;
            Bgr a = new Bgr(color.B - Math.Floor(color.B * 匹配度), color.G - Math.Floor(color.G * 匹配度), color.R - Math.Floor(color.R * 匹配度));
            Bgr b = new Bgr(color.B + Math.Floor(color.B * 匹配度), color.G + Math.Floor(color.G * 匹配度), color.R + Math.Floor(color.R * 匹配度));
            var c = parBitmap.ToImage<Bgr, byte>().InRange(a, b);
            for (int i = 0; i < c.Rows; i++)
            {
                for (int j = 0; j < c.Cols; j++)
                {
                    if (!c[i, j].Equals(new Gray(0)))
                    {
                        return new Point(j, i);
                    }
                }
            }
            return new Point(-1, -1);
        }

        private static OCRResult ocrR;
        private static PaddleOCREngine ocrE;
        static 图像_()
        {
            ocrR = new OCRResult();
            ocrE = new PaddleOCREngine(null, new OCRParameter());
        }

        public static string 识别文字(Rectangle rect)
        {
            using (var b = new Bitmap(rect.Width, rect.Height))
            using (Graphics g = Graphics.FromImage(b))
            {
                g.CopyFromScreen(new Point(rect.Left, rect.Top), new Point(0, 0), new Size(rect.Width, rect.Height));
                ocrR = ocrE.DetectText(b);
                return ocrR.Text.Trim();
            }
        }
        public static string 识别文字(Bitmap 图像)
        {
            ocrR = ocrE.DetectText(图像);
            return ocrR.Text.Trim();
        }
        public static string 识别文字(Image<Bgr, byte> 图像)
        {
            ocrR = ocrE.DetectText(图像.ToBitmap());
            return ocrR.Text.Trim();
        }

        public static string 识别文字(int 搜索x, int 搜索y, int 搜索x2, int 搜索y2)
        {
            if (搜索x2 <= 搜索x || 搜索y2 <= 搜索y) return "";
            var 搜索范围 = Rectangle.FromLTRB(搜索x, 搜索y, 搜索x2, 搜索y2);
            return 识别文字(搜索范围);
        }
        public static string 识别文字(Rectangle 搜索范围, string color, double 匹配度 = 0.9)
        {
            using (var b = new Bitmap(搜索范围.Width, 搜索范围.Height))
            using (Graphics g = Graphics.FromImage(b))
            {
                g.CopyFromScreen(new Point(搜索范围.Left, 搜索范围.Top), new Point(0, 0), new Size(搜索范围.Width, 搜索范围.Height));
                return 文字图像处理(b, 图像_.编译十六进制BGR颜色为ARGB颜色(color));
            }
            string 文字图像处理(Bitmap 图像, Color 颜色)
            {
                匹配度 = 1 - 匹配度;
                var b = 图像.ToImage<Bgr, byte>().InRange(new Bgr(颜色.B - 颜色.B * 匹配度, 颜色.G - 颜色.G * 匹配度, 颜色.R - 颜色.R * 匹配度), new Bgr(颜色.B + 颜色.B * 匹配度, 颜色.G + 颜色.G * 匹配度, 颜色.R + 颜色.R * 匹配度)).ToBitmap<Gray, byte>();
                return ocrE.DetectText(b).Text.Trim();
            }
        }

        public static string 识别文字(int 搜索x, int 搜索y, int 搜索x2, int 搜索y2, string color, double 匹配度 = 0.9)
        {
            if (搜索x2 <= 搜索x || 搜索y2 <= 搜索y) return "";
            var 搜索范围 = Rectangle.FromLTRB(搜索x, 搜索y, 搜索x2, 搜索y2);
            return 识别文字(搜索范围, color, 匹配度);
        }
        public static List<TextBlock> 查找文字(int 搜索x, int 搜索y, int 搜索x2, int 搜索y2)
        {
            if (搜索x2 <= 搜索x || 搜索y2 <= 搜索y) return null;
            var 搜索范围 = Rectangle.FromLTRB(搜索x, 搜索y, 搜索x2, 搜索y2);
            using (var b = new Bitmap(搜索范围.Width, 搜索范围.Height))
            using (Graphics g = Graphics.FromImage(b))
            {
                g.CopyFromScreen(new Point(搜索范围.Left, 搜索范围.Top), new Point(0, 0), new Size(搜索范围.Width, 搜索范围.Height));
                ocrR = ocrE.DetectText(b);
                return ocrR.TextBlocks;
            }
        }
        public static List<TextBlock> 查找文字(Rectangle 范围)
        {
            return 查找文字(范围.Left, 范围.Top, 范围.Right, 范围.Bottom);
        }

        public delegate bool 比较函数_(string s);

        public static Point 查找文字(Rectangle 范围, 比较函数_ func)
        {
            var tb = 查找文字(范围.Left, 范围.Top, 范围.Right, 范围.Bottom);
            if (tb != null)
            {
                foreach (var n in tb)
                {
                    if (func(n.Text.Trim()))
                    {
                        return new Point(范围.X + n.BoxPoints[0].X, 范围.Y + n.BoxPoints[0].Y);
                    }
                }
            }
            return new Point(-1, -1);
        }
        public static Point 查找文字(int X, int Y, int X2, int Y2, 比较函数_ func)
        {
            var tb = 查找文字(X, Y, X2, Y2);
            if (tb != null)
            {
                foreach (var n in tb)
                {
                    if (func(n.Text.Trim()))
                    {
                        return new Point(X + n.BoxPoints[0].X, Y + n.BoxPoints[0].Y);
                    }
                }
            }
            return new Point(-1, -1);
        }

        public static Point 查找文字(string 内容, int X, int Y, int X2, int Y2)
        {
            var tb = 查找文字(X, Y, X2, Y2);
            if (tb != null)
            {
                foreach (var n in tb)
                {
                    if (n.Text.Trim() == 内容)
                    {
                        return new Point(X + n.BoxPoints[0].X, Y + n.BoxPoints[0].Y);
                    }
                }
            }
            return new Point(-1, -1);
        }

        public static Point 查找文字(string 内容, Rectangle 范围)
        {
            var tb = 查找文字(范围);
            if (tb != null)
            {
                foreach (var n in tb)
                {
                    if (n.Text.Trim() == 内容)
                    {
                        return new Point(范围.Left + n.BoxPoints[0].X, 范围.Top + n.BoxPoints[0].Y);
                    }
                }
            }
            return new Point(-1, -1);
        }
    }
}
